/*******************************************************

Problems faced : passing multiple arguments to thread.
MSG_WAITin recv
********************************************************/

/************* Iterative seeder ****************/
#include "header.h"
#include "dstruct.h"

#include<sys/signal.h>
#include<sys/time.h>
#include<sys/resource.h>
#include<sys/wait.h>
#include<sys/errno.h>

#include<pthread.h>
#include<unistd.h>


#define SEND_BUFF 1024

#define BUF_SIZE 128

#define DEBUG

static void *argbuff;

void main_seeder(struct file_part *head,int partial)
{
	pthread_t	new_thread;	      
	pthread_attr_t	thread_attr;
/**************Doubt should i define it here or change this to take it from DS *************/
	struct sockaddr_in seed_server_sin;	 //seeder socketaddr struct
	struct sockaddr_in leech_client_sin;	 // leecher socketaddr struct  	
	int alen,t;  
	int	msocket, ssocket; // master and slave sockets

	memset(&seed_server_sin,0,sizeof(seed_server_sin) );
	memset(&leech_client_sin,0,sizeof(leech_client_sin) );
	
	seed_server_sin.sin_family = AF_INET;
	seed_server_sin.sin_addr.s_addr = INADDR_ANY;
	seed_server_sin.sin_port = htons(SEEDERSERVERPORT);
	
	//Create server socket.
	msocket = socket(PF_INET, SOCK_STREAM,0);
	if (msocket < 0){	
		printf("\n Cannot create the socket ");
		fflush(stdout);
		exit(1);
	}
     // Bind socket 
	if (bind(msocket, (struct sockaddr *)&seed_server_sin, sizeof(seed_server_sin)) < 0){
		printf("\n ERROR ! Cannot bind the socket ");
		fflush(stdout);
		exit(1);
	}
    if (listen(msocket, 3) < 0){
		printf("\n ERROR ! Cannot listen the socket ");
		fflush(stdout);
		exit(1);
	}
	printf("\n Waiting for leecher connections. ");
	fflush(stdout);
	while (1) 
	{
		(void) pthread_attr_init(&thread_attr);
	    (void) pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
#ifdef DEBUG
	    printf("\n After setting attributes detach state\n");
		fflush(stdout);
#endif
       for(t=0;t<NUMBER_OF_THREADS;t++) 
	   {
		printf("\nWaiting for leecher requests...\n");
		fflush(stdout);
		alen = sizeof(leech_client_sin);
		ssocket = accept(msocket, (struct sockaddr *)&leech_client_sin, &alen);

       /*         
		if (ssocket < 0)
			{
				if (errno == EINTR)
					continue;
				printf("\naccept: %s\n", strerror(errno));
				fflush(stdout);
			}
			*/
#ifdef DEBUG
         printf("\ncreating arguments for threading connections");
         fflush(stdout);
#endif         
/*        argbuff=malloc(sizeof ssocket + sizeof head);
		memcpy(argbuff,&ssocket,sizeof ssocket);
		memcpy(argbuff+sizeof ssocket,head, sizeof head);
*/
 /*        seed_arguments[t].socket=ssocket;
         
          seed_arguments[t].head=head;
         // memcpy(seed_arguments->head,head,sizeof(head));
  */
#ifdef DEBUG
         printf("\n Passing  slave Socket Number to function as: %d",ssocket);
         fflush(stdout);
  /*       printf("\n Copied values of socket: %d",seed_arguments[t].socket);
         fflush(stdout);
  */
#endif
 /*     if (pthread_create(&new_thread, &thread_attr, (void * (*)(void *))seeder_server,(void *)(seed_arguments)) < 0)
		{
		printf("\n ERROR ! Cannot Create new thread. ");
		fflush(stdout);
		}
*/	
		switch (fork())//create a new child process to handle requests.
		{
			case 0:	// CHILD process
				
				(void) close(msocket);//Close master socket
				if(partial == 0)
					seeder_server(ssocket,head);
				else
					partial_seeder_server(ssocket);
				(void) close(ssocket);
				exit(0);
                       
			default: /* if parent process */
				(void) close(ssocket);
				break;
			case -1:
				printf("fork");
		}
	

	  }
	}

}

void seeder_server(int sock,struct file_part *head)
{
	struct file_part *temp = head;
	char ack_buff;
	char p_ack='P';
	char msg[50],buffer[SEND_BUFF];
	char filename[30],part[20];
	int i =0,len=0,part_num,no_of_pieces=0,size_counter=0;
	int part_size,read_count=0;
	size_t bytes_read=0;
	char * field1,*field2;
	pthread_mutex_t safe_seed=PTHREAD_MUTEX_INITIALIZER; 
#ifdef DEBUG
	printf("\n Received Slave Socket Number as: %d",sock);
	printf("\n Filename in head node :%s", head->filename);
	printf("\nWaiting for file request from leecher.");
	fflush(stdout);
#endif
   recv(sock,msg,sizeof(msg),0); // receive the filename and part no from leecher.
   //parse the incoming message into tokens
   
#ifdef DEBUG
	printf("\nfile request from leecher received ... parsing %s.",msg);	
#endif
	field1=strtok(msg,",");
	field2=strtok(NULL,"$");
	strcpy(filename,field1);
	strcpy(part,field2);	
	part_num = atoi(part);
#ifdef DEBUG
	printf("\nfile part requested by leecher is part:%s filename %d.",filename,part_num);	
	fflush(stdout);
#endif	
	//calculate the number of pieces present in a file
	while(temp){
		no_of_pieces++;
		temp=temp->next;
	}
	temp = head;
	
	while(temp->part_num != part_num){
		temp=temp->next; // go to the requested file part node
	}
#ifdef DEBUG	
	printf("\nfile part in list %s.%d, size %d",temp->filename,temp->part_num,temp->file_size);	
	fflush(stdout);
#endif
	//part_size = temp->file_size - (no_of_pieces* PARTSIZE);

	pthread_mutex_unlock(&safe_seed);

	temp->data_ptr = fopen(filename, "rb");
	if(!temp->data_ptr){
		printf("ERROR: Unable to open %s.No such file or directory\n",filename);
		exit(1);
	}
#ifdef DEBUG	
	printf("\nCalculating offset");
	fflush(stdout);
#endif
	fseek(temp->data_ptr,0,SEEK_END);
	part_size = (int)ftell(temp->data_ptr);
	rewind(temp->data_ptr);
	
	//pthread_mutex_lock(&safe_seed);
#ifdef DEBUG
	printf("\nSetting offset");
	fflush(stdout);
#endif
	//moving to the proper offset	
	fseek(temp->data_ptr,((part_num-1)*PARTSIZE),SEEK_SET);
#ifdef DEBUG
	printf("\nOoffset is :%d",((part_num-1)*PARTSIZE));
	fflush(stdout);
	printf("\nSize_counter initially is :%d and part size:%d",size_counter,temp->file_size);
	fflush(stdout);
#endif

//	send(sock,&p_ack,sizeof(p_ack),0); // Ack that i am strting seeding
	while(size_counter != temp->file_size){
		pthread_mutex_lock(&safe_seed);
#ifdef DEBUG		
		printf("\nReading from file...");
		fflush(stdout);
#endif
		bytes_read = fread(buffer,sizeof(char),SEND_BUFF,temp->data_ptr);
#ifdef DEBUG
		printf("\nBytes read from file :%d",bytes_read);
		fflush(stdout);
#endif
		pthread_mutex_unlock(&safe_seed);

		size_counter += bytes_read;
#ifdef DEBUG		
		printf("\nSize Couter is :%d",size_counter);
		fflush(stdout);
#endif
		read_count++;
#ifdef DEBUG		
		printf("\nSending data");
		fflush(stdout);
#endif
		send(sock,buffer,bytes_read,0);
		/*if(size_counter == temp->file_size){
			break;
		}*/
		printf("\nwaiting for ack");
		fflush(stdout);
		recv(sock,&ack_buff,sizeof(ack_buff),MSG_WAITALL); // reason for &ack_buff: it is a char , but recv needs pointer
		if(ack_buff == 'P'){
			printf("\nP ack received\n");
			continue;
		}
		else if(ack_buff == 'N'){
			printf("\nN received");
			fflush(stdout);
			fseek(temp->data_ptr,-bytes_read,SEEK_CUR);
			size_counter -= bytes_read;
			read_count--;
		}
	}

	pthread_mutex_lock(&safe_seed);
	fclose(temp->data_ptr);
	pthread_mutex_unlock(&safe_seed);
	close(sock);
	printf("\nSocket Closed\n");
	fflush(stdout);
}

void partial_seeder_server(int sock)//,struct file_part *head)
{
	//struct file_part *temp = head;
	FILE *fp;
	crc filecrc;
	char ack_buff,fp_buff = 'P',fnp_buff = 'N';
	char msg[50],buffer[SEND_BUFF];
	char filename[30],part[20],readfile[30];
	int i =0,len=0,part_num,no_of_pieces=0,size_counter=0;
	int part_size,read_count=0;
	size_t bytes_read=0;
	char * field1,*field2;
	pthread_mutex_t safe_seed=PTHREAD_MUTEX_INITIALIZER; 
#ifdef DEBUG
	printf("\n Received Slave Socket Number as: %d",sock);
	//printf("\n Filename in head node :%s", head->filename);
	printf("\nWaiting for file request from leecher.");
	fflush(stdout);
#endif
	recv(sock,msg,sizeof(msg),0); // receive the filename and part no from leecher.
	//parse the incoming message into tokens

#ifdef DEBUG
	printf("\nfile request from leecher received ... parsing %s.",msg);	
#endif
	field1=strtok(msg,",");
	field2=strtok(NULL,"$");
	strcpy(filename,field1);
	strcpy(part,field2);	
	part_num = atoi(part);

	//calculate crc for this piece
	sprintf(readfile,"%s.part.%d",filename,part_num);
	filecrc = calculatecrc(readfile);
#ifdef DEBUG
	printf("\nfile part requested by leecher is %s",readfile);
	fflush(stdout);
#endif	
	//calculate the number of pieces present in a file
/*	while(temp){*/
/*		no_of_pieces++;*/
/*		temp=temp->next;*/
/*	}*/
/*	temp = head;*/
/*	*/
/*	while(temp->part_num != part_num){*/
/*		temp=temp->next; // go to the requested file part node*/
/*	}*/
#ifdef DEBUG	
//	printf("\nfile part in list %s.%d, size %d",temp->filename,temp->part_num,temp->file_size);	
//	fflush(stdout);
#endif
	//part_size = temp->file_size - (no_of_pieces* PARTSIZE);

	pthread_mutex_unlock(&safe_seed);

	fp = fopen(readfile, "rb");
	if(!fp){//file not present
		printf("ERROR: Unable to open %s.No such file or directory\n",readfile);
		send(sock,&fnp_buff,sizeof(fnp_buff),0);
		exit(1);
	}
	send(sock,&fp_buff,sizeof(fp_buff),0);
	recv(sock,&fp_buff,sizeof(fp_buff),0);
	if(fp_buff == 'S')
		printf("\nReceived %c",fp_buff);

#ifdef DEBUG
//	printf("\nCalculating offset");
//	fflush(stdout);
#endif
	
	fseek(fp,0,SEEK_END);
	part_size = (int)ftell(fp);
	rewind(fp);
	
	//pthread_mutex_lock(&safe_seed);
#ifdef DEBUG
//	printf("\nSetting offset");
//	fflush(stdout);
#endif
	//moving to the proper offset	
//	fseek(temp->data_ptr,((part_num-1)*PARTSIZE),SEEK_SET);
#ifdef DEBUG
//	printf("\noffset is :%d",((part_num-1)*PARTSIZE));
//	fflush(stdout);
//	printf("\nSize_counter initially is :%d and part size:%d",size_counter,temp->file_size);
//	fflush(stdout);
#endif
	while(size_counter != part_size){
		pthread_mutex_lock(&safe_seed);
#ifdef DEBUG		
		printf("\nReading from file...");
		fflush(stdout);
#endif
		bytes_read = fread(buffer,sizeof(char),SEND_BUFF,fp);
#ifdef DEBUG
		printf("\nBytes read from file :%d",bytes_read);
		fflush(stdout);
#endif
		pthread_mutex_unlock(&safe_seed);

		size_counter += bytes_read;
#ifdef DEBUG		
		printf("\nSize Couter is :%d",size_counter);
		fflush(stdout);
#endif
		read_count++;
#ifdef DEBUG		
		printf("\nSending data");
		fflush(stdout);
#endif
		send(sock,buffer,bytes_read,0);
		/*if(size_counter == temp->file_size){
			break;
		}*/
		printf("\nwaiting for ack");
		fflush(stdout);
		recv(sock,&ack_buff,sizeof(ack_buff),MSG_WAITALL); // reason for &ack_buff: it is a char , but recv needs pointer
		if(ack_buff == 'P'){
			printf("\nP ack received\n");
			//continue;
		}
		else if(ack_buff == 'N'){
			printf("\nN received");
			fflush(stdout);
			fseek(fp,-bytes_read,SEEK_CUR);
			size_counter -= bytes_read;
			read_count--;
		}
	}

	pthread_mutex_lock(&safe_seed);
	fclose(fp);
	pthread_mutex_unlock(&safe_seed);
	close(sock);
	printf("\nSocket Closed\n");
	fflush(stdout);
}

